
#include <tinyalsa/asoundlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>
#include <android/log.h>


#include <unistd.h>
#include <stdbool.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <semaphore.h>
#include <pthread.h>
#include <jni.h>
#include "ring_buffer.h"
#include "android_utils.h"

#include "test.h"

extern JNIEnv *JniEnv;
extern jclass JniClass;
extern JavaVM *java_vm;
extern jmethodID TtReceiveVoiceByteFromJNI_ID;
extern jmethodID TtReceiveVoiceShortFromJNI_ID;

#define thread_net_audio 0

struct loop_ctl_t
{
    pthread_t thread;
    sem_t   sem;
    bool is_exit;
    pthread_mutex_t lock;
    void *dev;
    bool state;
    struct pcm *pcm;
    struct ring_buffer *ring_buf;
};

struct dsp_loop_t {
    struct loop_ctl_t in;//
    struct loop_ctl_t out;
    struct loop_ctl_t in_codec;
    struct loop_ctl_t out_codec;
    bool state;
};

static struct dsp_loop_t *loop = NULL;
static struct ring_buffer *ring_buf = NULL;
static struct ring_buffer *ring_buf_codec = NULL;
static int tiantong_audio_frame_size;//可由上位机协议定义
static int loop_mode = 0;

#define pcm_delay 600 //buf延时160ms
#define AUDIO_LOOP_MIN_DATA_MS 500
#define MIN_BUFFER_TIMS_MS 20

#define pcm_card 	0
#define pcm_device	2 //0号声卡 2号设备为天通语音设备
#define audio_channels	2
#define audio_rate	8000
#define audio_period_size 160  //320
#define audio_period_count	2

static struct pcm_config pcm_config_audio = {
        .channels = audio_channels,//2
        .rate = audio_rate,
        .period_size = audio_period_size,//160
        .period_count = audio_period_count,//2
        .format = PCM_FORMAT_S16_LE,
};

int test_calculation_ring_buffer_size(int ms,struct pcm_config *config){
    int size=ms*config->rate*config->channels*2/1000;
    int i=31;
    while(i){
        if((size & (1<<i))!=0)
            break;
        else
            i--;
    }

    if(i<=0)
        return 0;
    else
        return 1<<(i+1);
}




int test_recieve_ringaudio_byte(JNIEnv *env, jbyteArray audio_frame, jint size){

    int num_read,ret;
    char *buf;
    int available_size;
    int count_wait = 20;
    struct loop_ctl_t *out = &(loop->out_codec);

    buf = malloc(size);
    if(!buf){
        LOGE("Unable to allocate %d bytes\n",size);
        free(buf);
        return -1;
    }
    if(out == NULL){
        LOGE("loop out ctl is NULL");
        return -1;
    }

    if(loop_mode != 0){
        LOGE("loop_mode is err");
        return -2;
    }

    available_size = ring_buffer_len(out->ring_buf);
    while((count_wait--) && (available_size < size)){
        LOGD("recieve_ringaudio_byte  ring_buffer_len too small available_size:%d\n",
             available_size);
        usleep(MIN_BUFFER_TIMS_MS * 1000);//延时10ms
    }

    num_read = ring_buffer_get(out->ring_buf, (void *)buf, size);
    if(num_read > 0){
        (*env)->SetByteArrayRegion(env,audio_frame, 0, num_read, (jbyte *)buf);
        if((*env)->ExceptionCheck(env)){
            LOGD("recieve_audio_byte SetByteArrayRegion err\n");
            (*env)->ExceptionClear(env);
            goto err;
        }
    }else{
        LOGE("recieve_ringaudio_byte ring_buffer_get err");
        goto err;
    }

    if(buf != NULL){
        free(buf);
    }
    LOGD("recieve_ringaudio_byte ok");
    return num_read;

    err:
    if(buf != NULL){
        free(buf);
    }
    return -1;

    return 0;
}

int test_send_audio_to_ringbuf(char *buf, int size){

    struct loop_ctl_t *in = &loop->in_codec;

    if((in == NULL) && (size <= 0)){
        LOGE("send_audio_to_ringbuf in_codec NULL size <= 0");
        return -1;
    }

    producer_proc(in->ring_buf, (unsigned char *) buf, (unsigned int) size);
    return 0;

    return 0;
}

int test_tingtong_audio_loop_stop(){

    struct loop_ctl_t *ctl = NULL;
    LOGD("tingtong_audio_loop_stop");
    if((false == loop->state) && (NULL == loop)){
        LOGW("tingtong_audio_loop_stop faled, loop is not work");
        return -1;
    }
    //1:设置loop结构体状态退出
    loop->in.is_exit = true;
    loop->out.is_exit = true;
    loop->in_codec.is_exit = true;
    loop->out_codec.is_exit = true;

    //2:pthread_join接收线程退出
    pthread_join(loop->in.thread, NULL);
    LOGD("tingtong_audio_loop_stop in.thread%d",__LINE__);
#if thread_net_audio
    pthread_join(loop->in_codec.thread, NULL);
    LOGD("tingtong_audio_loop_stop in_codec.thread%d",__LINE__);
#endif

    if(loop_mode == 0) {
        pthread_join(loop->out_codec.thread, NULL);
        LOGD("tingtong_audio_loop_stop out_codec.thread%d",__LINE__);
    }

    pthread_join(loop->out.thread, NULL);
    LOGD("tingtong_audio_loop_stop out.thread%d",__LINE__);

    //3:释放锁mux pthread_mutex_unlock
    //loop->in.is_exit后线程退出，设置退出状态，重复做一次
    ctl = &loop->in;
    pthread_mutex_lock(&(ctl->lock));
    ctl->is_exit = true;
    ctl->dev = NULL;
    if(ctl->pcm != NULL){
        pcm_close(ctl->pcm);
        ctl->pcm = NULL;
        LOGD("tingtong_audio_loop_stop close pcm in %d",__LINE__);
    }
    pthread_mutex_unlock(&(ctl->lock));

    ctl = &loop->out;
    pthread_mutex_lock(&(ctl->lock));
    ctl->is_exit = true;
    ctl->dev = NULL;
    if(ctl->pcm != NULL){
        pcm_close(ctl->pcm);
        ctl->pcm = NULL;
        LOGD("tingtong_audio_loop_stop close pcm out %d",__LINE__);
    }
    pthread_mutex_unlock(&(ctl->lock));

    if(loop->out.ring_buf != NULL){
        ring_buffer_free(loop->out.ring_buf);
    }
    LOGD("tingtong_audio_loop_stop stop in and out ring\n");

    ctl = &loop->in_codec;
    pthread_mutex_lock(&(ctl->lock));
    ctl->is_exit = true;
    ctl->dev = NULL;
    if(ctl->pcm != NULL){
        pcm_close(ctl->pcm);
        ctl->pcm = NULL;
        LOGD("tingtong_audio_loop_stop close pcm in_codec %d",__LINE__);
    }
    pthread_mutex_unlock(&(ctl->lock));

    ctl = &loop->out_codec;
    pthread_mutex_lock(&(ctl->lock));
    ctl->is_exit = true;
    ctl->dev = NULL;
    if(ctl->pcm != NULL){
        pcm_close(ctl->pcm);
        ctl->pcm = NULL;
        LOGD("tingtong_audio_loop_stop close pcm out_codec %d",__LINE__);
    }
    pthread_mutex_unlock(&(ctl->lock));
    LOGD("tingtong_audio_loop_stop stop in_codec and out_codec ring\n");

    if(loop->out_codec.ring_buf != NULL){
        ring_buffer_free(loop->out_codec.ring_buf);
    }

    loop->in.state = false;
    loop->out.state = false;
    loop->in_codec.state = false;
    loop->out_codec.state = false;
    loop->state = false;

    pthread_mutex_destroy(&(loop->out.lock));
    pthread_mutex_destroy(&(loop->in.lock));
    pthread_mutex_destroy(&(loop->out_codec.lock));
    pthread_mutex_destroy(&(loop->in_codec.lock));
    LOGD("tingtong_audio_loop_stop exit ===ok=====");

    if(loop != NULL){
        free(loop);
    }

    return 0;

    return 0;
}

static void *test_tiantong_audio_loop_rx_thread(void *args) {//
    struct loop_ctl_t *in = (struct loop_ctl_t *)args;
    struct pcm *pcm = in->pcm;
    char *buffer = NULL;
    int num_read = 0;
    int size = 0;
    int bytes_read = 0;
    int ret;
    JNIEnv *env = NULL;
    bool is_attached = false;

    pthread_mutex_lock(&(in->lock));
    if(NULL == java_vm){
        LOGE("tiantong_audio_loop_rx_thread java_vm is null");
        goto ERR;
    }
    if(NULL == in->pcm){
        LOGE("tiantong_audio_loop_rx_thread pcm is null");
        goto ERR;
    }
    in->state = true;
    size = pcm_frames_to_bytes(pcm, pcm_config_audio.period_size);//pcm_bytes_to_frames(pcm,pcm_get_buffer_size(pcm))
    LOGD("tiantong_audio_loop_rx_thread size:%d,period_size:%d\n",size, pcm_bytes_to_frames(pcm,pcm_get_buffer_size(pcm)));

    buffer = (char *)malloc(size);
    if(!buffer){
        LOGE("tiantong_audio_loop_rx_thread Unable to allocate %d bytes\n", size);
        goto ERR;
    }

    sem_wait(&in->sem);
    if((*java_vm)->AttachCurrentThread(java_vm, &env, NULL) == 0) {
        is_attached = true;
        LOGD("tiantong_audio_loop_rx_thread AttachCurrentThread ok");
        while (in->is_exit == false) {
            ret = pcm_mmap_read(pcm, buffer, size);
            if (!num_read) {//读取pcm语音ok
                bytes_read += size;
                producer_proc(in->ring_buf, (unsigned char *) buffer, (unsigned int) size);
            } else {
                LOGE("tiantong_audio_loop_rx_thread pcm_mmap_read err delay 300ms\n");
                //pcm card出错处理 pcm_mmap_read处理错，lc1860平台必须out in俩个设备同时关闭再打开
                usleep(300 * 1000);//延时300ms
            }
        }
    } else{
        LOGD("tiantong_audio_loop_rx_thread AttachCurrentThread failed");
        is_attached = false;
    }

    if((is_attached == true) && (java_vm != NULL)){
        (*java_vm)->DetachCurrentThread(java_vm);
    }

    in->state = false;
    if(buffer){
        free(buffer);
        buffer = NULL;
    }

    pthread_mutex_unlock(&(in->lock));
    LOGD("tiantong_audio_loop_rx_thread exit success");
    return NULL;

    ERR:
    if((is_attached == true) && (java_vm != NULL)){
        (*java_vm)->DetachCurrentThread(java_vm);
    }

    if(buffer){
        free(buffer);
        buffer = NULL;
    }

    if(in->pcm){
        pcm_close(in->pcm);
        in->pcm = NULL;
    }
    in->state = false;
    pthread_mutex_unlock(&(in->lock));
    LOGE("tiantong_audio_loop_rx_thread exit err");


    return NULL;
}

static  void *test_tiantong_audio_loop_rx_thread_codec(void *args){

    struct loop_ctl_t *in = (struct loop_ctl_t *)args;
    char *buffer = NULL;
    int num_read = 0;
    int size = 0;
    int bytes_read = 0;
    int ret;
    bool is_attached = false;
    JNIEnv *env = NULL;

    pthread_mutex_lock(&(in->lock));
    in->state = true;
    size = tiantong_audio_frame_size;
    buffer = (char *)malloc(size);
    if(!buffer){
        LOGE("Unbale to allocate %d bytes\n", size);
        goto ERR;
    }
    if(java_vm == NULL){
        LOGE("tiangtong_audio_loop_tx_thread java_vm is null ");
        goto ERR;
    }

    sem_wait(&in->sem);
    if((*java_vm)->AttachCurrentThread(java_vm, &env, NULL) == 0) {
        LOGE("tiangtong_audio_loop_tx_thread AttachCurrentThread is ok ");
        is_attached = true;
        while (in->is_exit == false) {
            /*
             * 回调java读取上位机数据，写入buf
             * */
            //num_read = jni_java(buffer,size);
            if (!num_read) {
                producer_proc(in->ring_buf, (unsigned char *) buffer, (unsigned int) size);
            } else {
                usleep(MIN_BUFFER_TIMS_MS * 1000);//延时10ms
            }
        }
    } else{
        LOGE("tiangtong_audio_loop_tx_thread AttachCurrentThread failed ");
        is_attached = false;
    }

    ERR:
    if((is_attached == true) && (java_vm != NULL)){
        (*java_vm)->DetachCurrentThread(java_vm);
        is_attached = false;
    }

    if(buffer){
        free(buffer);
        buffer = NULL;
    }
    in->state = false;
    pthread_mutex_unlock(&(in->lock));
    LOGE("tiantong_audio_loop_rx_thread_codec exit err");
    return NULL;
}

static void *test_tiangtong_audio_loop_tx_thread(void *args){

    struct loop_ctl_t *out = (struct  loop_ctl_t *)args;
    struct pcm *pcm = out->pcm;
    char *buffer = NULL;
    int num_read = 0;
    int size = 0;
    int ret = -1;
    int write_count = 0;
    int no_data_count = 0;
    int available_size;
    int count_wait = 20;
    JNIEnv *env = NULL;
    bool is_attached = false;

    pthread_mutex_lock(&(out->lock));
    out->state = true;

    if(java_vm == NULL){
        LOGE("tiangtong_audio_loop_tx_thread java_vm is null ");
        goto ERR;
    }

    if(NULL == out->pcm){
        LOGE("tiangtong_audio_loop_tx_thread pcm is null ");
        goto ERR;
    }

    size = pcm_frames_to_bytes(pcm, pcm_config_audio.period_size);
    //size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
    buffer = (char *)malloc(size);
    if(!buffer){
        LOGE("tiangtong_audio_loop_tx_thread Unable to allocate %bytes\n",size);
        goto ERR;
    }
    sem_wait(&out->sem);
    if((*java_vm)->AttachCurrentThread(java_vm, &env, NULL) == 0) {
        is_attached = true;
        LOGD("tiangtong_audio_loop_tx_thread AttachCurrentThread ok");
        while (out->is_exit == false) {
            //1：先判断ringbuf是否有size大小可读的缓存
            available_size = ring_buffer_len(out->ring_buf);
            if ((available_size < size) && count_wait) {
                LOGD("tiangtong_audio_loop_tx_thread  ring_buffer_len too small available_size:%d\n",
                     available_size);
                usleep(MIN_BUFFER_TIMS_MS * 1000);//延时10ms
                count_wait--;//等待20次，如果20次后,ringbuf还是没填充数据,将剩余的data发送
                continue;
            }

            count_wait = 20;//打开检测
            num_read = ring_buffer_get(out->ring_buf, (void *) buffer, size);
            if (num_read > 0) {
                no_data_count = 0;
                ret = pcm_mmap_write(pcm, buffer, num_read);
                if (ret) {//out设备写失败可以关闭再重新打开
                    LOGE("tiangtong_audio_loop_tx_thread error pcm_mmap_write :%s\n",
                         pcm_get_error(pcm));
                    /*
                    pcm_close(pcm);
                    out->pcm = pcm_open(pcm_card, pcm_device, PCM_OUT | PCM_MMAP | PCM_NOIRQ |PCM_MONOTONIC, &pcm_config_audio);
                    if(!out->pcm || !pcm_is_ready(out->pcm)){
                        LOGE("tiangtong_audio_loop_tx_thread Unable to open PCM dvice %u (%s) \n",
                             2, pcm_get_error(out->pcm));
                        goto  ERR;
                    }
                    pcm = out->pcm;
                     */
                } else {
                    write_count += num_read;
                }
            } else {
                usleep(MIN_BUFFER_TIMS_MS * 1000);//延时10ms
                no_data_count++;
            }
        }
    } else{
        is_attached = false;
        LOGD("tiangtong_audio_loop_tx_thread AttachCurrentThread failed");
    }

    if((is_attached == true) && (java_vm != NULL)){
        is_attached = false;
        (*java_vm)->DetachCurrentThread(java_vm);
    }

    if(buffer){
        free(buffer);
        buffer = NULL;
    }
    out->state = false;
    pthread_mutex_unlock(&(out->lock));

    return NULL;

    ERR:
    if((is_attached == true) && (java_vm != NULL)){
        (*java_vm)->DetachCurrentThread(java_vm);
    }

    if(buffer){
        free(buffer);
        buffer = NULL;
    }

    if(out->pcm){
        pcm_close(out->pcm);
        out->pcm = NULL;
    }

    out->state = false;
    pthread_mutex_unlock(&(out->lock));
    LOGE("tiangtong_audio_loop_tx_thread exit err");
    return NULL;
}

static void *test_tiangtong_audio_loop_tx_thread_codec(void *args){

    struct loop_ctl_t *out = (struct loop_ctl_t *)args;
    char *buffer = NULL;
    int num_read = 0;
    int size = 0;
    int ret = -1;
    int write_count = 0;
    int no_data_count = 0;
    int available_size;
    int count_wait = 20;
    JNIEnv *env = NULL;
    bool is_attached = false;
    jbyteArray voice_to_java = NULL;

    pthread_mutex_lock(&(out->lock));
    out->state = true;
    size = tiantong_audio_frame_size;
    buffer = (char *)malloc(size);
    if(!buffer){
        LOGE("tiangtong_audio_loop_tx_thread Unable to allocate %d bytes\n", size);
        goto ERR;
    }
    sem_wait(&out->sem);

    if((java_vm == NULL) || (TtReceiveVoiceByteFromJNI_ID == NULL)){
        goto ERR;
    }

    // 将当前线程附加到Java虚拟机上，并获得当前线程相关的JNIEnv指针
    if((*java_vm)->AttachCurrentThread(java_vm, &env, NULL) == 0) {
        is_attached = true;
        LOGD("tiangtong_audio_loop_tx_thread_codec AttachCurrentThread success");
        voice_to_java = (*env)->NewByteArray(env, size);
        if(voice_to_java == NULL){
            LOGD("tiangtong_audio_loop_tx_thread_codec NewByteArray voice_to_java error");
            goto ERR;
        }

        while (out->is_exit == false) {
            //1：先判断ringbuf是否有size大小可读的缓存
            available_size = ring_buffer_len(out->ring_buf);
            if ((available_size < size) && count_wait) {//如果上位机不是按照config设置的大小发送，可能导致最后一帧没有
                LOGD("tiangtong_audio_loop_tx_thread  ring_buffer_len too small available_size:%d\n",
                     available_size);
                usleep(MIN_BUFFER_TIMS_MS * 1000);//延时10ms
                count_wait--;//等待20次，如果20次后,ringbuf还是没填充数据,将剩余的data发送
                continue;
            }

            count_wait = 20;//打开检测
            num_read = ring_buffer_get(out->ring_buf, (void *) buffer, size);
            if (num_read > 0) {
                //(*env)->SetByteArrayRegion(env, voice_to_java, 0, num_read, (jbyte *)buffer);
                //jni调用java方法
                (*env)->SetByteArrayRegion(env, voice_to_java, 0, num_read, (jbyte *)buffer);
                if((*env)->ExceptionCheck(env)){
                    LOGE("SetByteArrayRegion voice_to_java err");
                    (*env)->ExceptionClear(env);
                    goto ERR;
                }
                //反射给java
                (*env)->CallStaticVoidMethod(env, JniClass, TtReceiveVoiceByteFromJNI_ID, num_read, voice_to_java);
                if((*env)->ExceptionCheck(env)){
                    LOGE("CallStaticVoidMethod TtReceiveVoiceByteFromJNI_ID error\n");
                    (*env)->ExceptionClear(env);
                    goto  ERR;
                }
            } else {
                usleep(MIN_BUFFER_TIMS_MS * 1000);//延时10ms
                no_data_count++;
                write_count += num_read;
                LOGD("tiangtong_audio_loop_tx_thread write:%d, total:%d\n", num_read, write_count);
            }
        }
    }else{
        LOGD("tiangtong_audio_loop_tx_thread_codec AttachCurrentThread failed");
        is_attached = false;
    }

    ERR:
    if(voice_to_java != NULL){
        LOGD("DeleteLocalRef voice_to_java");
        (*env)->DeleteLocalRef(env, voice_to_java);
    }
    if((is_attached == true) && (java_vm != NULL)){
        // 将线程从Java虚拟机上剥离
        is_attached = false;
        (*java_vm)->DetachCurrentThread(java_vm);
    }
    if(buffer){
        free(buffer);
        buffer = NULL;
    }

    out->state = false;
    pthread_mutex_unlock(&(out->lock));
    return NULL;

    return  NULL;
}

int test_tiangtong_audio_loop_start(int mode){

    int size = 0;
    int min_buffer_size = 0;
    int delay_size = 0;
    int ret = 0;

    loop_mode = mode;
    //先检查是否正在工作
    if(true == loop->state){
        LOGW("tiangtong_audio_loop_start failed, loop is working now");
        return -1;
    }

    //audio_dsp_loop_start
    //1:初始化ringbuf
    //a:获取最小的ringbuf大小
    size = calculation_ring_buffer_size(pcm_delay, &pcm_config_audio);
    min_buffer_size = (AUDIO_LOOP_MIN_DATA_MS * pcm_config_audio.rate * pcm_config_audio.channels * 2 / 1000);
    if(size < min_buffer_size){
        size = calculation_ring_buffer_size(AUDIO_LOOP_MIN_DATA_MS, &pcm_config_audio);
    }
    delay_size = (pcm_delay * pcm_config_audio.rate * pcm_config_audio.channels * 2 /1000);
    if(size < delay_size){
        size = delay_size * 2;
    }

    ring_buf = ring_buffer_init(size, delay_size);
    if(NULL == ring_buf){
        goto error;
    }

    ring_buf_codec = ring_buffer_init(size, delay_size);
    if(NULL == ring_buf_codec){
        goto error;
    }
    //2:线程锁mux初始化
    if(NULL != loop){
        memset(loop, 0, sizeof (struct dsp_loop_t));
    }else{ //初始化loop
        loop = calloc(1, sizeof (struct dsp_loop_t));//calloc 和malloc的区别,都是分配连续的空间，但是calloc会清楚分配的空间0，可用分配n块
        if(!loop){
            LOGE("malloc dsp_loop_t failed ,size:%d", sizeof (struct dsp_loop_t));
            goto error;
        }
        memset(loop, 0, sizeof (struct dsp_loop_t));
    }

    if(pthread_mutex_init(&(loop->in.lock), NULL) != 0){
        LOGE("Failed pthread_mutex_init loop->in.lock,errno:%u,%s",
             errno, strerror(errno));
        goto error;
    }
    if(pthread_mutex_init(&(loop->out.lock), NULL) != 0){
        LOGE("Failed pthread_mutex_init loop->out.lock,errno:%u,%s",
             errno, strerror(errno));
        goto error;
    }
#if thread_net_audio
    if(pthread_mutex_init(&(loop->in_codec.lock), NULL) != 0){
        LOGE("Failed pthread_mutex_init loop->in_codec.lock,errno:%u,%s",
             errno, strerror(errno));
        goto error;
    }
#endif
    if(pthread_mutex_init(&(loop->out_codec.lock), NULL) != 0){
        LOGE("Failed pthread_mutex_init loop->out_codec.lock,errno:%u,%s",
             errno, strerror(errno));
        goto error;
    }

    loop->in.ring_buf = ring_buf_codec;//in的生产者buf,ring_buf_codec生产者
    loop->out.ring_buf = ring_buf;//ring_buf消费者
    loop->in_codec.ring_buf = ring_buf;
    loop->out_codec.ring_buf = ring_buf_codec;

    loop->in.state = true;
    loop->out.state = true;
    loop->in_codec.state = true;
    loop->out_codec.state = true;

    loop->in.is_exit = false;
    loop->out.is_exit = false;
    loop->in_codec.is_exit = false;
    loop->out_codec.is_exit = false;

    sem_init(&loop->in.sem, 0, 1);//shared=0:用于多线程的同步 sem_wait value > 0则减一立即返回，=0阻塞
    sem_init(&loop->out.sem, 0, 1);
    sem_init(&loop->in_codec.sem, 0, 1);
    sem_init(&loop->out_codec.sem, 0, 1);

    //3:声卡初始化
    loop->out.pcm = pcm_open(pcm_card, pcm_device, PCM_OUT | PCM_MMAP | PCM_NOIRQ |PCM_MONOTONIC, &pcm_config_audio);
    if(!loop->out.pcm || !pcm_is_ready(loop->out.pcm)){
        LOGD("Unable to open PCM play device (%s)\n",
             pcm_get_error(loop->out.pcm));
        goto error;
    }

    loop->in.pcm = pcm_open(pcm_card, pcm_device, PCM_IN |PCM_MMAP | PCM_NOIRQ|PCM_MONOTONIC, &pcm_config_audio);
    if(!loop->in.pcm || !pcm_is_ready(loop->in.pcm)){
        LOGI("Unable to open PCM record device (%s)\n",
             pcm_get_error(loop->in.pcm));
        goto error;
    }

    loop->in_codec.pcm = NULL;
    loop->out_codec.pcm = NULL;

    tiantong_audio_frame_size = pcm_frames_to_bytes(loop->in.pcm, pcm_config_audio.period_size);
    loop->state = true;
//tiantong_audio_loop_rx_thread 生产(来自天通)  《---》tiangtong_audio_loop_tx_thread_codec消费(给到上位机)
//tiantong_audio_loop_rx_thread_codec生产(上位机) 《===》tiangtong_audio_loop_tx_thread 消费(天通)
    //4:创建loop语音线程,ringbuf生产者
    if(pthread_create(&loop->in.thread, NULL, test_tiantong_audio_loop_rx_thread, (void *)&(loop->in))){
        LOGE("tiantong_audio_loop_start creating rx thread failed !!!\n");
        goto error;
    }
    //ringbuf生产者
#if thread_net_audio
    if(pthread_create(&loop->in_codec.thread, NULL, test_tiantong_audio_loop_rx_thread_codec, (void *)&(loop->in_codec))){
        LOGE("tiantong_audio_loop_start creating codec rx thread failed !!!\n");
        goto error;
    }
#endif
    //消费者
    if(pthread_create(&loop->out.thread, NULL, test_tiangtong_audio_loop_tx_thread,(void *)&(loop->out))){
        LOGE("tiantong_audio_loop_start creating tx thread failed !!!!");
        goto error;
    }
    //消费者
    if(loop_mode == 0) {
        LOGD("tiangtong_audio_loop_start loop_mode 0 to tiangtong_audio_loop_tx_thread_codec");
        if (pthread_create(&loop->out_codec.thread, NULL, test_tiangtong_audio_loop_tx_thread_codec,
                           (void *) &(loop->out_codec))) {
            LOGE("tiangtong_audio_loop_start creating codec tx thread failed !!!");
            goto error;
        }
    }
    return 0;

    error:
    if(NULL != loop->out.pcm){
        pcm_close(loop->out.pcm);
        loop->out.pcm = NULL;
    }
    if(NULL != loop->in.pcm){
        pcm_close(loop->in.pcm);
        loop->in.pcm = NULL;
    }

    if(NULL != loop){
        free(loop);
    }
    if(NULL != ring_buf_codec){
        ring_buffer_free(ring_buf_codec);
    }
    if(NULL != ring_buf){
        ring_buffer_free(ring_buf);
    }

    loop->in.state = false;
    loop->out.state = false;
    loop->in_codec.state = false;
    loop->out_codec.state = false;

    loop->state = false;
    return -1;
}

